package com.sp.business.normalization.impl.mf2;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Lexes a subset of Lua used in conf files.
 * 
 * @author mtaylor
 * 
 */
public class Mf2Lexer {
    private static final int START = 0;
    private static final int LABEL = 1;
    private static final int DIGIT = 2;
    private static final int STRING = 3;

    public static class Token {
        TokenType type;
        String text;

        Token(char c, TokenType type) {
            this.text = new String(new char[] { c });
            this.type = type;
        }

        Token(String text, TokenType type) {
            this.text = text;
            this.type = type;
        }

        public String toString() {
            return type.name() + ":" + text;
        }
    }

    public static enum TokenType {
        LABEL, 
        DIGIT, 
        STRING, 
        BOOLEAN, 
        RT_BRACKET, 
        LT_BRACKET, 
        RT_BRACE, 
        LT_BRACE, 
        DELIMITER, 
        EQUAL;
    }

    public List<Token> tokenize(BufferedReader input) throws IOException {
        List<Token> list = new ArrayList<Token>();

        StringBuilder buffer = new StringBuilder();

        int state = START;
        int c = input.read();
        while (c >= 0) {
            switch (state) {
            case START: // start
                if (Character.isLetter(c)) {
                    buffer.append((char) c);
                    state = LABEL;
                } else if (Character.isDigit(c)) {
                    buffer.append((char) c);
                    state = DIGIT;
                } else if (Character.isWhitespace(c)) {
                    // skip whitespace
                } else if (c == '{') {
                    list.add(new Token((char) c, TokenType.LT_BRACE));
                } else if (c == '}') {
                    list.add(new Token((char) c, TokenType.RT_BRACE));
                } else if (c == '[') {
                    list.add(new Token((char) c, TokenType.LT_BRACKET));
                } else if (c == ']') {
                    list.add(new Token((char) c, TokenType.RT_BRACKET));
                } else if (c == ',' || c == ';') {
                    list.add(new Token((char) c, TokenType.DELIMITER));
                } else if (c == '"') {
                    buffer.append((char) c);
                    state = STRING;
                } else if (c == '=') {
                    list.add(new Token((char) c, TokenType.EQUAL));
                }
                c = input.read();
                break;
            case LABEL:
                if (Character.isJavaIdentifierPart(c)) {
                    buffer.append((char) c);
                    c = input.read();
                } else {
                    String text = buffer.toString();
                    if ("true".equals(text) || "false".equals(text)) {
                        list.add(new Token(text, TokenType.BOOLEAN));
                    } else {
                        list.add(new Token(text, TokenType.LABEL));
                    }
                    buffer.delete(0, buffer.length());
                    state = START;
                }
                break;
            case DIGIT:
                if (Character.isDigit(c)) {
                    buffer.append((char) c);
                    c = input.read();
                } else {
                    list.add(new Token(buffer.toString(), TokenType.DIGIT));
                    buffer.delete(0, buffer.length());
                    state = START;
                }
                break;
            case STRING:
                if (c != '"') {
                    buffer.append((char) c);
                } else {
                    buffer.append((char) c);
                    String string = trimQuotes(buffer.toString());
                    list.add(new Token(string, TokenType.STRING));
                    buffer.delete(0, buffer.length());
                    state = START;
                }
                c = input.read();
                break;
            default:
                throw new RuntimeException("Invalid state reached in Mf2Lexer!");
            }
        }
        return list;
    }
    
    private static String trimQuotes(String string) {
        if(string != null && string.startsWith("\"") && string.endsWith("\"")) {
            string = string.substring(1, string.length() - 1);
        }
        return string;
    }
}
